package org.example;

import lombok.Data;
import org.example.entity.Person;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.task.TaskExecutor;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @description: 多线程处理数据，并获取处理结果（有bug）
 */
@SpringBootTest
public class ExampleApplicationTest2 {

    @Resource
    private TaskExecutor taskExecutor;
    // 批次执行线程大小
    int threadNum = 10;

    @Test
    public void main() throws InterruptedException {
        List<Person> personList = initData();
        if (CollectionUtils.isEmpty(personList)) {
            return;
        }
        long startTime = System.currentTimeMillis();

        // 定义栅栏
        CyclicBarrier cyclicBarrier = new CyclicBarrier(threadNum, () -> {
        });

        // 处理成功的数据
        List<Person> successList = new ArrayList<>();
        // 处理错误的数据
        List<Person> errorList = new ArrayList<>();

        // 循环次数
        int forNumber = personList.size() % threadNum == 0 ? personList.size() / threadNum : (personList.size() / threadNum) + 1;
        // 计数器等于线程数，计时到每个线程都执行完成任务
        CountDownLatch countDownLatch = new CountDownLatch(threadNum);
        // 处理集合的索引，从0开始
        AtomicInteger listIndex = new AtomicInteger();
        for (int i = 0; i < threadNum; i++) {
            taskExecutor.execute(() -> {
                for (int j = 0; j < forNumber; j++) {
                    try {
                        if (listIndex.get() < personList.size()) {
                            // 这里不要先 getAndIncrement 再 get，多线程下会导致 get的值可能不是当前线程 ++ 后的，就会导致同一个索引的数据处理了多次
                            Person person = personList.get(listIndex.getAndIncrement());
                            boolean state = importData(person);
                            if (state) {
                                successList.add(person);
                            } else {
                                errorList.add(person);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            // 确保每个线程都在屏障前等待
                            cyclicBarrier.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                // 每个线程执行完成后，计数器 -1
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        long endTime = System.currentTimeMillis();
        System.out.println("耗时" + (endTime - startTime));
        System.out.println("处理成功数量：" + successList.size());
        System.out.println("处理失败数量：" + errorList.size());
    }

    private boolean importData(Person person) {
        // 模拟处理耗时，20-29ms随机数
        int expend = (int) (Math.random() * 10) + 20;
        try {
            TimeUnit.MILLISECONDS.sleep(expend);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 性别为女的处理成功
        if ("女".equals(person.getGender())) {
            return true;
        }
        return false;
    }

    private List<Person> initData() {
        List<Person> list = new ArrayList<>();
        for (int i = 1; i <= 2001; i++) {
            Person obj = new Person();
            obj.setId(i);
            obj.setGender(i % 2 == 0 ? "男" : "女");
            obj.setName("老王-" + i);
            list.add(obj);
        }
        return list;
    }
}